#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <nice2d/application.h>
#include <nice2d/sprite.h>
#include <nice2d/components/imagemanager.h>
#include <nice2d/animation.h>
#include <nice2d/deserializerfactory.h>

class AnimationData
{
public:
    bool started = false;
    bool singleShot = true;
    size_t index = 0;
    float duration = 120.0f;
    float currentTime = 0.0f;

    std::shared_ptr<ImageDescriptor> imageDescriptor;
    std::vector<SpritePointer> items;

    std::shared_ptr<Sprite> createSprite(const std::string &file, const sf::IntRect &area)
    {
        auto textureManager = GET_COMPONENT(Application::getInstance(), ImageManager);
        auto sprite = std::make_shared<Sprite>();
        sprite->setBackgroundColor(sf::Color::Transparent);
        sprite->addSpriteImage(file, area);
        sprite->setSize(area.width, area.height);
        return sprite;
    }
};

Animation::Animation():
    data(new AnimationData())
{
}

Animation::~Animation()
{
}

void Animation::start()
{
    data->started = true;
    data->index = 0;
}

bool Animation::isStarted() const
{
    return data->started;
}

void Animation::pause()
{
    data->started = false;
}

bool Animation::isPaused() const
{
    return !data->started;
}

void Animation::clear()
{
    data->imageDescriptor.reset();
}

void Animation::setSingleShot(bool single)
{
    data->singleShot = single;
}

bool Animation::getSingleShot() const
{
    return data->singleShot;
}

void Animation::setDurationPerFrame(float duration)
{
    data->duration = duration;
}

void Animation::setImageDescriptor(std::shared_ptr<ImageDescriptor> descriptor)
{
    if (!descriptor)
        return;

    setSize(descriptor->imageSize.x, descriptor->imageSize.y);

    data->imageDescriptor = descriptor;

    auto itr = data->imageDescriptor->areas.begin();
    while (itr != data->imageDescriptor->areas.end()) {
        auto sprite = data->createSprite(data->imageDescriptor->file, *itr);
        data->items.push_back(sprite);
        itr ++;
    }
}

void Animation::onUpdateEntity(float deltaTime)
{
    if (isPaused())
        return;

    data->currentTime += deltaTime;

    if (data->index < 0)
        data->index = 0;

    if (data->currentTime > data->duration) {
        data->currentTime = 0.0f;
        data->index ++;
    }

    if (data->index >= data->imageDescriptor->areas.size()) {
        if (data->singleShot) {
            data->index --;
            finished();
        } else
            data->index = 0;
    } else {
        if (!data->singleShot && data->index >= data->imageDescriptor->areas.size())
            data->index = 0;
    }
}

void Animation::onDrawEntity(sf::RenderTarget &target, sf::RenderStates states) const
{
    if (!data->items.empty())
        target.draw(*data->items[data->index], states);
}

PTree Animation::serialize()
{
    PTree node;
    node.add_child(getObjectName(), Entity::serialize());

    node.add("start", data->started);
    node.add("singleShot", data->singleShot);
    node.add("durationPerFrame", data->duration);

    if (data->imageDescriptor)
        node.add_child("imageDescriptor", data->imageDescriptor->serialize());
    return node;
}

void Animation::deserialize(const PTree &node)
{
    Entity::deserialize(node);
    data->started = node.get<bool>("start");
    data->singleShot = node.get<bool>("singleShot");
    data->duration = node.get<float>("durationPerFrame");

    auto descriptor = DeserializerFactory::deserialize<ImageDescriptor>("imageDescriptor", node);
    if (descriptor)
        setImageDescriptor(descriptor);
}

